﻿using Inaction;
using Inaction.Mapping;
using Inaction.Resource.Properties;
using Inaction.Util;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Common;
using System.ComponentModel;
using System.Data;
using System.Collections;
using Inaction.Core;
using Inaction.ServiceContract;

namespace Inaction
{
    public abstract partial class Entity<T> : IEntitySavable, IEditableObject
    {
        #region 数据操作
        public virtual void OnSaving() { }
        public virtual void OnSaved() { }
        public void Save()
        {
            OnSaving();
            if (!this.IsDeleted)
            {
                var error = string.Empty;
                if (CheckError(out error, null))
                {
                    throw new Exception(error);
                }
            }
            var t = (T)Inaction.Proxy.Save(this);
            if (AppConfig.Instance.RunLocation == RunLocation.Client)
            {
                this.Clone(t);
            }
            this.MarkNone();
            OnSaved();
        }
        protected virtual DbProvider DbProvider
        {
            get { return Inaction.DbProvider.Instance; }
        }

        public void DoSave()
        {
            using (var connection = this.DbProvider.Connection)
            {
                var trans = connection.BeginTransaction();
                try
                {
                    foreach (var item in FirstTransData)
                    {
                        item.Save(trans);
                    }
                    Save(trans);
                    foreach (var item in LastTransData)
                    {
                        item.Save(trans);
                    }
                    trans.Commit();
                    ClearTransData();
                    MarkNone();
                }
                catch (Exception ex)
                {
                    trans.Rollback();
                    Loger.WriteLog(ex);
                    throw ex;
                }
            }
        }
        public virtual void Save(IDbTransaction trans)
        {
            try
            {
                if (!IsDirty) return;
                if (!this.IsDeleted)
                {
                    var error = string.Empty;
                    if (CheckError(out error, trans)) throw new Exception(error);
                }
                if (IsDeleted)
                {
                    SaveChild(trans);
                    SaveSelf(trans);
                }
                else
                {
                    SaveSelf(trans);
                    SaveChild(trans);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
        protected void SaveSelf(IDbTransaction trans)
        {
            try
            {
                OnSaving(trans);
                if (IsSelfDirty || IsDeleted)
                {
                    var sql = EntitySqlStatement.GetSaveStatement((T)this);
                    using (var command = DbProvider.CreateCommand(trans, sql))
                    {
                        if (command.ExecuteNonQuery() != 1)
                        {
                            throw new Exception(string.Format(Resources.SaveFailedHasDirty, ClassInfo.DisplayName));
                        }
                        Inaction.Util.Loger.WriteLog(sql);
                    }
                }
                OnSaved(trans);
            }
            catch (Exception ex)
            {
                Inaction.Util.Loger.WriteLog(ex);
                throw ex;
            }
        }
        protected void SaveChild(IDbTransaction trans)
        {
            if (_childs.Count > 0)
            {
                foreach (var item in _childs)
                {
                    item.Value.Save(trans);
                }
            }
        }
        private void ClearDeletedEntites()
        {
            foreach (var child in this._childs)
            {
                child.Value.ClearDeletedEntites();
            }
        }
        #endregion

        #region ISavable 成员
        bool checkDirtyOnSave = true;
        [Browsable(false)]
        public bool CheckDirtyOnSave
        {
            get { return checkDirtyOnSave; }
            set { checkDirtyOnSave = value; }
        }
        [Browsable(false)]
        public new bool IsDirty
        {
            get { return IsNew || IsDeleted || IsSelfDirty || _childs.Any(p => p.Value.IsDirty); }
        }
        public virtual void OnSaving(IDbTransaction trans) { }
        public virtual void OnSaved(IDbTransaction trans) { }

        List<Core.ISavable> firstTransData = new List<Core.ISavable>();
        [Browsable(false)]
        public List<Core.ISavable> FirstTransData
        {
            get { return firstTransData; }
        }

        List<Core.ISavable> lastTransData = new List<Core.ISavable>();
        [Browsable(false)]
        public List<Core.ISavable> LastTransData
        {
            get { return lastTransData; }
        }

        public void ClearTransData()
        {
            this.firstTransData.Clear();
            this.lastTransData.Clear();
            foreach (var item in _childs)
            {
                item.Value.ClearTransData();
            }
        }

        public virtual void BeginEdit()
        {
        }

        public virtual void CancelEdit()
        {
        }

        public virtual void EndEdit()
        {
        }

        public virtual void MarkNone()
        {
            foreach (var child in _childs.Values)
            {
                child.MarkNone();
            }
            isNew = false;
            isDeleted = false;
            isSelfDirty = false;
            PropertyTrack();
        }

        void IDeletable.ClearDeletedEntites()
        {
            this.ClearDeletedEntites();
        }
        #endregion

        #region IClonable
        [Browsable(false)]
        public int Key
        {
            get;
            private set;
        }
        public T Clone()
        {
            var t = new T();
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            foreach (var p in classInfo.Properties)
            {
                p.SetFieldValue(t, p.GetFieldValue(this));
            }
            foreach (var child in this._childs)
            {
                t._childs.Add(child.Key, (IEntityList)child.Value.Clone());
            }
            return t;
        }
        public void Clone(IClonable source)
        {
            var entity = source as ModelBase;
            if (entity == null) throw new Exception(Inaction.Resource.Properties.Resources.CloneMustBetweenSameType);
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            var properties = classInfo.Properties;
            var entityClassInfo = ClassInfo.LoadInfo(entity.GetType());
            foreach (var p in properties)
            {
                if (entityClassInfo.Properties.Any(t => t.Name == t.Name && t.UnderlyingType == p.UnderlyingType))
                {
                    p.SetFieldValue(this, p.GetFieldValue(entity));
                }
            }
            var temp = entity as T;
            if (temp != null)
            {
                foreach (var child in temp._childs)
                {
                    if (_childs.ContainsKey(child.Key))
                    {
                        var item = _childs.FirstOrDefault(p => p.Key == child.Key);
                        item.Value.Clone(child.Value);
                    }
                }
            }
        }

        object IClonable.Clone()
        {
            return Clone();
        }
        #endregion
    }
}